home *** CD-ROM | disk | FTP | other *** search
/ Mac Magazin/MacEasy 11 / Mac Magazin and MacEasy Magazine CD - Issue 11.iso / Sharewarebibliothek / Entwickler / WASTE 1.1b1 Distribution / WASTE Source / WELineLayout.p < prev    next >
Text File  |  1995-06-01  |  19KB  |  659 lines

  1. unit WELineLayout;
  2.  
  3. { WASTE PROJECT: }
  4. { Line Layout, Getting and Setting Variables, etc. }
  5.  
  6. { Copyright © 1993-1994 Marco Piovanelli }
  7. { All Rights Reserved }
  8.  
  9. interface
  10.     uses
  11.         WEDrawing;
  12.  
  13.     procedure WEStopInlineSession (hWE: WEHandle);
  14.     function _WERemoveLine (lineIndex: LongInt;
  15.                                     pWE: WEPtr): OSErr;
  16.     function _WERecalBreaks (var startLine, endLine: LongInt;
  17.                                     hWE: WEHandle): OSErr;
  18.     procedure _WERecalSlops (firstLine, lastLine: LongInt;
  19.                                     hWE: WEHandle);
  20.     function WEGetTextLength (hWE: WEHandle): LongInt;
  21.     function WECountLines (hWE: WEHandle): LongInt;
  22.     function WEGetHeight (startLine, endLine: LongInt;
  23.                                     hWE: WEHandle): LongInt;
  24.     procedure WEGetSelection (var selStart, selEnd: LongInt;
  25.                                     hWE: WEHandle);
  26.     procedure WEGetDestRect (var destRect: LongRect;
  27.                                     hWE: WEHandle);
  28.     procedure WESetDestRect (var destRect: LongRect;
  29.                                     hWE: WEHandle);
  30.     procedure WEGetViewRect (var viewRect: LongRect;
  31.                                     hWE: WEHandle);
  32.     procedure WESetViewRect (var viewRect: LongRect;
  33.                                     hWE: WEHandle);
  34.     function WEGetText (hWE: WEHandle): Handle;
  35.     function WEGetChar (offset: LongInt;
  36.                                     hWE: WEHandle): Char;
  37.     function WEGetAlignment (hWE: WEHandle): SignedByte;
  38.     function WEUseText (text: Handle;
  39.                                     hWE: WEHandle): OSErr;
  40.  
  41. implementation
  42.     uses
  43.         FixMath;
  44.  
  45.     procedure WEStopInlineSession (hWE: WEHandle);
  46.         var
  47.             pWE: WEPtr;
  48.     begin
  49.         pWE := hWE^;
  50.  
  51. { call FixTSMDocument only if the inline input area is actually "open" }
  52.         if (pWE^.tsmAreaStart <> kInvalidOffset) then
  53.             if (pWE^.tsmReference <> nil) then
  54.                 if (FixTSMDocument(pWE^.tsmReference) <> noErr) then
  55.                     ;
  56.     end;  { WEStopInlineSession }
  57.  
  58.     function _WERemoveLine (lineIndex: LongInt;
  59.                                     pWE: WEPtr): OSErr;
  60.  
  61. { remove the specified element from the line array }
  62.  
  63.     begin
  64.  
  65. { do the removal (errors returned by _WERemoveSlot can be safely ignored) }
  66.         _WERemoveLine := _WERemoveSlot(pWE^.hLines, lineIndex, SizeOf(LineRec));
  67.  
  68. { decrement line count }
  69.         pWE^.nLines := pWE^.nLines - 1;
  70.  
  71.     end;  { _WERemoveLine }
  72.  
  73.     function _WEInsertLine (lineIndex: LongInt;
  74.                                     var theLine: LineRec;
  75.                                     pWE: WEPtr): OSErr;
  76.  
  77. { insert the specified element in the line array }
  78.  
  79.         var
  80.             err: OSErr;
  81.     begin
  82.         _WEInsertLine := noErr;
  83.  
  84. { do the insertion }
  85.         err := _WEInsertSlot(pWE^.hLines, @theLine, lineIndex, SizeOf(theLine));
  86.         if (err <> noErr) then
  87.             begin
  88.                 _WEInsertLine := err;
  89.                 Exit(_WEInsertLine);
  90.             end;
  91.  
  92. { increment line count }
  93.         pWE^.nLines := pWE^.nLines + 1;
  94.  
  95.     end;  { _WEInsertLine }
  96.  
  97.     procedure _WEBumpOrigin (lineIndex: LongInt;
  98.                                     deltaOrigin: LongInt;
  99.                                     pWE: WEPtr);
  100.         var
  101.             pOrigin: LongIntPtr;
  102.             nLines: LongInt;
  103.     begin
  104.         pOrigin := @pWE^.hLines^^[lineIndex].lineOrigin;
  105.  
  106. { loop through the line run array adjusting the lineOrigin fields }
  107.         nLines := pWE^.nLines;
  108.         while (lineIndex <= nLines) do
  109.             begin
  110.                 pOrigin^ := pOrigin^ + deltaOrigin;
  111.                 pOrigin := LongIntPtr(LongInt(pOrigin) + SizeOf(LineRec));
  112.                 lineIndex := lineIndex + 1;
  113.             end;
  114.     end;  { _WEBumpOrigin }
  115.  
  116.     function _WEFindLineBreak (lineStart: LongInt;
  117.                                     hWE: WEHandle): LongInt;
  118.  
  119. { Find where to break the line beginning at lineStart }
  120. { the WE record and the text must be already locked }
  121. { the current graphics port must be already set up correctly }
  122.  
  123.         var
  124.             pWE: WEPtr;
  125.             pText: Ptr;
  126.             offset, breakOffset: LongInt;
  127.             textLength: LongInt;
  128.             remainingLength: LongInt;
  129.             segmentStart, segmentEnd: LongInt;
  130.             runIndex: LongInt;
  131.             runInfo: WERunInfo;
  132.             pixelWidth: Fixed;
  133.             script, previousScript: ScriptCode;
  134.             isBreak: Boolean;
  135.     begin
  136.         pWE := hWE^;
  137.         offset := lineStart;
  138.         pText := Ptr(LongInt(pWE^.hText^) + offset);
  139.         remainingLength := pWE^.textLength - offset;
  140.  
  141. { find the style run index corresponding to the first segment on this line }
  142.         runIndex := _WEOffsetToRun(offset, hWE);
  143.  
  144. { initialize pixelWidth to the width of the destination rectangle, as a Fixed quantity }
  145.         pixelWidth := BSL(pWE^.destRect.right - pWE^.destRect.left, 16);
  146.  
  147. { STYLE SEGMENT LOOP }
  148.         repeat
  149.  
  150. { get style run information for the current style run }
  151.             _WEGetIndStyle(runIndex, runInfo, hWE);
  152.             runIndex := runIndex + 1;
  153.  
  154. { set text attributes in the graphics port }
  155.             TextFont(runInfo.runAttrs.runStyle.tsFont);
  156.             __TextFace(runInfo.runAttrs.runStyle.tsFace);
  157.             TextSize(runInfo.runAttrs.runStyle.tsSize);
  158.  
  159. { if we're handling multiscript text, keep track of script boundaries }
  160.             if BTST(pWE^.flags, weFNonRoman) then
  161.                 begin
  162.  
  163. { what is the script for this segment? }
  164.                     script := FontToScript(runInfo.runAttrs.runStyle.tsFont);
  165.  
  166. { have we crossed a script run boundary in the middle of a line? }
  167.                     if (runInfo.runStart > offset) and (script <> previousScript) then
  168.                         begin
  169.  
  170. { leave behind the all previous segments on this line }
  171.                             offset := runInfo.runStart;
  172.                             pText := Ptr(LongInt(pWE^.hText^) + offset);
  173.                             remainingLength := pWE^.textLength - offset;
  174.                         end;
  175.  
  176.                     previousScript := script;
  177.                 end;  { if non-Roman }
  178.  
  179. { we'll pass textLength as the second parameter to the line break hook }
  180. { although this parameter is declared as a long, StyledLineBreak uses only }
  181. { the low word, so make sure it doesn't trespass the 32,767 byte threshold! }
  182.             textLength := _WEPinInRange(remainingLength, 0, maxint);
  183.  
  184. { calculate segmentStart and segmentEnd relative to offset }
  185.             segmentStart := _WEPinInRange(runInfo.runStart - offset, 0, textLength);
  186.             segmentEnd := _WEPinInRange(runInfo.runEnd - offset, 0, textLength);
  187.  
  188. { set breakOffset to a non-zero value for the first script run on the line, }
  189. { set it to zero for all subsequent script runs }
  190.             breakOffset := Integer(offset = lineStart);
  191.  
  192.             if (runInfo.runAttrs.runStyle.tsObject <> kNullObject) then
  193.                 begin
  194.  
  195. { EMBEDDED OBJECT }
  196. { subtract object width from pixelWidth }
  197.                     pixelWidth := pixelWidth - BSL(WEObjectDescHandle(runInfo.runAttrs.runStyle.tsObject)^^.objectSize.h, 16);
  198.  
  199. { stop looping if pixelWidth has gone negative }
  200.                     isBreak := (pixelWidth < 0);
  201.  
  202.                     if (isBreak) then
  203.                         breakOffset := segmentStart        { break line before the object }
  204.                     else
  205.                         breakOffset := segmentEnd;        { break line after the object }
  206.  
  207.                 end
  208.             else
  209.                 begin
  210.  
  211. { REGULAR TEXT }
  212.  
  213. {$IFC WASTE_DEBUG}
  214.                     _WEAssert(pWE^.lineBreakHook <> nil, 'Missing LineBreak Hook');
  215. {$ENDC}
  216.  
  217. { let the line break hook do the work for us }
  218.                     isBreak := (CallWELineBreakProc(pText, textLength, segmentStart, segmentEnd, pixelWidth, breakOffset, hWE, pWE^.lineBreakHook) <> smBreakOverflow);
  219.  
  220.                 end;
  221.  
  222. { break the line anyway when we reach the end of the text }
  223.             if (segmentEnd >= remainingLength) then
  224.                 isBreak := true;
  225.  
  226.         until (isBreak);
  227.  
  228. { return the offset from lineStart to the break point }
  229.         _WEFindLineBreak := (offset - lineStart) + breakOffset;
  230.  
  231.     end;  { _WEFindLineBreak }
  232.  
  233.     procedure _WECalcHeights (rangeStart, rangeEnd: LongInt;
  234.                                     var lineAscent, lineDescent: Integer;
  235.                                     hWE: WEHandle);
  236.  
  237. { Find the maximum ascent and descent values between rangeStart and rangeEnd }
  238. { the WE record must be already locked }
  239. { the current graphics port must be already set up correctly }
  240.  
  241.         var
  242.             runIndex: LongInt;
  243.             runInfo: WERunInfo;
  244.             runAscent, runDescent: Integer;
  245.     begin
  246.         lineAscent := 1;
  247.         lineDescent := 1;
  248.  
  249. { find the style run index corresponding to the first segment on this line }
  250.         runIndex := _WEOffsetToRun(rangeStart, hWE);
  251.  
  252. { STYLE SEGMENT LOOP }
  253.         repeat
  254.  
  255. { get style run information for the current style run }
  256.             _WEGetIndStyle(runIndex, runInfo, hWE);
  257.             runIndex := runIndex + 1;
  258.  
  259. { calculate ascent and descent (actually, descent + leading) values for this style run }
  260.             if (runInfo.runAttrs.runStyle.tsObject <> kNullObject) then
  261.                 begin
  262.  
  263. { EMBEDDED OBJECT }
  264.                     runAscent := WEObjectDescHandle(runInfo.runAttrs.runStyle.tsObject)^^.objectSize.v;
  265.                     runDescent := 0;
  266.                 end
  267.             else
  268.                 begin
  269.  
  270. { REGULAR TEXT }
  271.                     runAscent := runInfo.runAttrs.runAscent;
  272.                     runDescent := runInfo.runAttrs.runHeight - runAscent;
  273.                 end;
  274.  
  275. { save the maximum values in lineAscent and lineDescent }
  276.             if (runAscent > lineAscent) then
  277.                 lineAscent := runAscent;
  278.             if (runDescent > lineDescent) then
  279.                 lineDescent := runDescent;
  280.  
  281. { keep looping until we reach rangeEnd }
  282.         until (runInfo.runEnd >= rangeEnd);
  283.  
  284.     end;  { _WECalcHeights }
  285.  
  286.     function _WERecalBreaks (var startLine, endLine: LongInt;
  287.                                     hWE: WEHandle): OSErr;
  288.  
  289. { Recalculates line breaks, line heights and ascents for all the text or for a portion of it. }
  290. { On entry, startLine and endLine define a range of lines to recalculate. }
  291. { On exit, startLine to endLine defines the range of lines actually recalculated }
  292. { the WE record must already be locked }
  293.  
  294.         label
  295.             1;
  296.         var
  297.             pWE: WEPtr;
  298.             pLine: LinePtr;
  299.             lineInfo, oldLineInfo: LineRec;
  300.             lineIndex: LongInt;
  301.             recalThreshold: LongInt;
  302.             lineOffset: LongInt;
  303.             lineAscent, lineDescent: Integer;
  304.             textHeight: LongInt;
  305.             saveTextLock: Boolean;
  306.             saveEnvironment: QDEnvironment;
  307.             err: OSErr;
  308.     begin
  309.         _WERecalBreaks := noErr;
  310.         pWE := hWE^;
  311.  
  312. { lock the text }
  313.         saveTextLock := _WESetHandleLock(pWE^.hText, true);
  314.  
  315. { find the character offset that must be necessarily reached before we can }
  316. { even consider the possibility of stopping the recalculation process }
  317. { this offset, recalThreshold, is the last character on endLine _before_ recalculation }
  318.         lineIndex := _WEPinInRange(endLine, 0, pWE^.nLines - 1);
  319.         recalThreshold := pWE^.hLines^^[lineIndex + 1].lineStart;
  320.  
  321. { we start recalculating line breaks from the line actually _preceding_ startLine, }
  322. { since editing startLine may cause part of its text to fit on the preceding line }
  323.         lineIndex := _WEPinInRange(startLine - 1, 0, pWE^.nLines - 1);
  324.  
  325. { find where in the text recalculation should begin }
  326.         lineInfo := pWE^.hLines^^[lineIndex];
  327.  
  328. { save the QuickDraw environment }
  329.         _WESaveQDEnvironment(pWE^.port, false, saveEnvironment);
  330.  
  331. { MAIN LINE BREAKING LOOP }
  332.         repeat
  333.  
  334. { find where to break the current line }
  335.             lineOffset := _WEFindLineBreak(lineInfo.lineStart, hWE);
  336.  
  337. { make sure we advance at least by one character (unless we reached the end of text) }
  338.             if (lineOffset <= 0) then
  339.                 if (lineInfo.lineStart < pWE^.textLength) then
  340.                     lineOffset := 1;
  341.  
  342. { calculate ascent and descent values for this line }
  343.             _WECalcHeights(lineInfo.lineStart, lineInfo.lineStart + lineOffset, lineAscent, lineDescent, hWE);
  344.  
  345. { save the maximum line ascent for this line in the line array }
  346.             pLine := @pWE^.hLines^^[lineIndex];
  347.             pLine^.lineAscent := lineAscent;
  348.  
  349. { increment counters (go to the next line array entry) }
  350.             lineIndex := lineIndex + 1;
  351.             lineInfo.lineStart := lineInfo.lineStart + lineOffset;
  352.             lineInfo.lineOrigin := lineInfo.lineOrigin + (lineAscent + lineDescent);
  353.             pLine := LinePtr(LongInt(pLine) + SizeOf(LineRec));
  354.  
  355. { compare the newly calculated line start with the old value }
  356. { if the new line start comes before the old line start, insert a new element }
  357.             oldLineInfo := pLine^;
  358.             if (lineIndex > pWE^.nLines) | (lineInfo.lineStart < oldLineInfo.lineStart) then
  359.                 begin
  360.                     err := _WEInsertLine(lineIndex, lineInfo, pWE);
  361.  
  362. { clean up and exit if we ran out of memory }
  363.                     if (err <> noErr) then
  364.                         begin
  365.                             _WERecalBreaks := err;
  366.                             goto 1;
  367.                         end;
  368.                 end
  369.             else
  370.                 begin
  371.  
  372. { overwrite the old element }
  373.                     pLine^.lineStart := lineInfo.lineStart;
  374.                     pLine^.lineOrigin := lineInfo.lineOrigin;
  375.  
  376. { remove all further elements which have a lineStart field }
  377. { less than or equal to the current one }
  378.                     while (lineIndex + 1 <= pWE^.nLines) & (lineInfo.lineStart >= pWE^.hLines^^[lineIndex + 1].lineStart) do
  379.                         err := _WERemoveLine(lineIndex + 1, pWE);
  380.  
  381. { if the new line start is the same as the old one... }
  382.                     if (lineInfo.lineStart = oldLineInfo.lineStart) then
  383.                         begin
  384.  
  385. { ...and recalThreshold has been reached, we can stop recalculating line breaks }
  386.                             if (lineInfo.lineStart >= recalThreshold) then
  387.                                 begin
  388. { although line breaks need not be changed from lineIndex on, }
  389. { the lineOrigin fields may need to be changed }
  390.                                     if (lineInfo.lineOrigin <> oldLineInfo.lineOrigin) then
  391.                                         _WEBumpOrigin(lineIndex + 1, lineInfo.lineOrigin - oldLineInfo.lineOrigin, pWE);
  392.  
  393. { exit from the line breaking loop }
  394.                                     goto 1;
  395.                                 end;
  396.                         end
  397.                     else
  398.                         begin
  399.  
  400. { otherwise, the new line start comes after the old line start... }
  401. { if the current line is the one preceding startLine, warn our caller about this }
  402.                             if ((lineIndex > 0) and (lineIndex = startLine)) then
  403.                                 startLine := lineIndex - 1;
  404.  
  405.                         end;
  406.                 end;
  407.  
  408.         until (lineInfo.lineStart >= pWE^.textLength);
  409.  
  410. 1:
  411. { calculate total text height }
  412.         textHeight := WEGetHeight(0, pWE^.nLines, hWE);
  413.  
  414. { quirk: if the last character in the text is a carriage return, the caret appears }
  415. { below the last line, so in this case we need to add the extra height to textHeight }
  416.         if (WEGetChar(pWE^.textLength - 1, hWE) = CHR(13)) then
  417.             textHeight := textHeight + WEGetHeight(pWE^.nLines - 1, pWE^.nLines, hWE);
  418.  
  419. { if total text height has changed, remember to call the scroll callback later }
  420.         if (textHeight <> pWE^.destRect.bottom - pWE^.destRect.top) then
  421.             BSET(pWE^.flags, weFDestRectChanged);
  422.  
  423. { set destRect.bottom to destRect.top + total text height }
  424.         pWE^.destRect.bottom := pWE^.destRect.top + textHeight;
  425.  
  426. { return through endLine the index of the last line affected by recalculation }
  427.         endLine := lineIndex - 1;
  428.  
  429. { make sure startLine isn't greater than endLine }
  430.         if (startLine > endLine) then
  431.             startLine := endLine;
  432.  
  433. { unlock the text }
  434.         if (_WESetHandleLock(pWE^.hText, saveTextLock)) then
  435.             ;
  436.  
  437. { restore the QuickDraw environment }
  438.         _WERestoreQDEnvironment(saveEnvironment);
  439.  
  440.     end;  { _WERecalBreaks }
  441.  
  442.     procedure _WERecalSlops (firstLine, lastLine: LongInt;
  443.                                     hWE: WEHandle);
  444.  
  445. { Calculates the lineSlop and lineJustAmount fields }
  446. { of the line array for the specified lines }
  447.  
  448.         var
  449.             pWE: WEPtr;
  450.             textLength: LongInt;
  451.             totalSlop, lineWidth: Integer;
  452.             totalProportion: Fixed;
  453.  
  454.         function SLCalcSlop (pLine: LinePtr;
  455.                                         pAttrs: WERunAttributesPtr;
  456.                                         pSegment: Ptr;
  457.                                         segmentStart, segmentLength: LongInt;
  458.                                         styleRunPosition: JustStyleCode): Boolean;
  459.             var
  460.                 segmentWidth: Integer;
  461.                 segmentProportion: Fixed;
  462.                 isEndOfLine: Boolean;
  463.         begin
  464.             SLCalcSlop := false;                { keep looping }
  465.  
  466. { see if this text segment ends with a carriage return, or if we've reached the }
  467. { end of the text (in which case we don't want any justification to take place) }
  468.             isEndOfLine := (segmentStart + segmentLength >= textLength) | (Ptr(LongInt(pSegment) + segmentLength - 1)^ = kEOL);
  469.  
  470. { if this is the first segment on the line, reset line totals }
  471.             if (styleRunPosition <= leftStyleRun) then
  472.                 begin
  473.                     totalSlop := lineWidth;
  474.                     totalProportion := 0;
  475.                 end;
  476.  
  477.             if (pAttrs^.runStyle.tsObject <> kNullObject) then
  478.                 begin
  479.  
  480. { EMBEDDED OBJECT }
  481. { segment width is just object width; no extra space can be applied for justification }
  482.                     segmentWidth := WEObjectDescHandle(pAttrs^.runStyle.tsObject)^^.objectSize.h;
  483.                     segmentProportion := 0;
  484.                 end
  485.             else
  486.                 begin
  487.  
  488. { REGULAR TEXT }
  489. { if this is the last segment on the line, strip trailing spaces }
  490.                     if (not Odd(styleRunPosition)) then
  491.                         segmentLength := VisibleLength(pSegment, segmentLength);
  492.  
  493. { measure this segment }
  494.                     segmentWidth := TextWidth(pSegment, 0, segmentLength);
  495.  
  496. { calculate the proportion of extra space to apply to this text segment }
  497.                     segmentProportion := PortionLine(pSegment, segmentLength, styleRunPosition, Point(kOneToOneScaling), Point(kOneToOneScaling));
  498.                 end;
  499.  
  500. { keep track of line totals }
  501.             totalSlop := totalSlop - segmentWidth;
  502.             totalProportion := totalProportion + segmentProportion;
  503.  
  504. { if this is the last segment on the line, save values in the line array }
  505.             if (not Odd(styleRunPosition)) then
  506.                 begin
  507.  
  508. { make sure slop is non-negative }
  509.                     if (totalSlop < 0) then
  510.                         totalSlop := 0;
  511.                     pLine^.lineSlop := totalSlop;
  512.  
  513.                     if (isEndOfLine) then
  514.                         pLine^.lineJustAmount := 0
  515.                     else
  516.                         pLine^.lineJustAmount := FixDiv(BSL(totalSlop, 16), totalProportion);
  517.  
  518.                 end;
  519.         end;  { SLCalcSlop }
  520.  
  521.     begin
  522.         pWE := hWE^;
  523.  
  524. { don't bother recalculating slops if the text is left-aligned }
  525.         if (pWE^.alignment = weFlushLeft) then
  526.             Exit(_WERecalSlops);
  527.  
  528.         textLength := pWE^.textLength;
  529.         lineWidth := pWE^.destRect.right - pWE^.destRect.left;
  530.  
  531. { calculate slop and normalized slop proportion for all lines }
  532.         _WESegmentLoop(firstLine, lastLine, SLCalcSlop, hWE);
  533.  
  534.     end;  { _WERecalSlops }
  535.  
  536.     function WEUseText (text: Handle;
  537.                                     hWE: WEHandle): OSErr;
  538.         var
  539.             pWE: WEPtr;
  540.             textLength: LongInt;
  541.             saveWELock: Boolean;
  542.     begin
  543.         WEUseText := noErr;
  544.  
  545. { lock the WE record }
  546.         saveWELock := _WESetHandleLock(hWE, true);
  547.         pWE := hWE^;
  548.  
  549. { install the text }
  550.         _WEForgetHandle(pWE^.hText);
  551.         pWE^.hText := text;
  552.         textLength := GetHandleSize(text);
  553.         pWE^.textLength := textLength;
  554.         pWE^.hRuns^^[pWE^.nRuns].runStart := textLength + 1;
  555.         pWE^.hLines^^[pWE^.nLines].lineStart := textLength;
  556.  
  557. { unlock the WE record }
  558.         if (_WESetHandleLock(hWE, saveWELock)) then
  559.             ;
  560.  
  561.     end;  { WEUseText }
  562.  
  563.     function WEGetAlignment (hWE: WEHandle): SignedByte;
  564.     begin
  565.         WEGetAlignment := hWE^^.alignment;
  566.     end;  { WEGetAlignment }
  567.  
  568.     procedure WEGetSelection (var selStart, selEnd: LongInt;
  569.                                     hWE: WEHandle);
  570.         var
  571.             pWE: WEPtr;
  572.     begin
  573.         pWE := hWE^;
  574.         selStart := pWE^.selStart;
  575.         selEnd := pWE^.selEnd;
  576.     end;  { WEGetSelection }
  577.  
  578.     procedure WESetDestRect (var destRect: LongRect;
  579.                                     hWE: WEHandle);
  580.     begin
  581.         hWE^^.destRect := destRect;
  582.     end;  { WESetDestRect }
  583.  
  584.     procedure WEGetDestRect (var destRect: LongRect;
  585.                                     hWE: WEHandle);
  586.     begin
  587.         destRect := hWE^^.destRect;
  588.     end;  { WEGetDestRect }
  589.  
  590.     procedure WESetViewRect (var viewRect: LongRect;
  591.                                     hWE: WEHandle);
  592.         var
  593.             pWE: WEPtr;
  594.             r: Rect;
  595.     begin
  596.         pWE := hWE^;
  597.         pWE^.viewRect := viewRect;
  598.  
  599. { keep the viewRgn in sync with the view rectangle }
  600.         WELongRectToRect(viewRect, r);
  601.         RectRgn(pWE^.viewRgn, r);
  602.  
  603.     end;  { WESetViewRect }
  604.  
  605.     procedure WEGetViewRect (var viewRect: LongRect;
  606.                                     hWE: WEHandle);
  607.     begin
  608.         viewRect := hWE^^.viewRect;
  609.     end;  { WEGetViewRect }
  610.  
  611.     function WEGetTextLength (hWE: WEHandle): LongInt;
  612.     begin
  613.         WEGetTextLength := hWE^^.textLength;
  614.     end;  { WEGetTextLength }
  615.  
  616.     function WECountLines (hWE: WEHandle): LongInt;
  617.     begin
  618.         WECountLines := hWE^^.nLines;
  619.     end;  { WECountLines }
  620.  
  621.     function WEGetHeight (startLine, endLine: LongInt;
  622.                                     hWE: WEHandle): LongInt;
  623.         var
  624.             pWE: WEPtr;
  625.             pLines: LineArrayPtr;
  626.             nLines: LongInt;
  627.     begin
  628.         pWE := hWE^;
  629.         pLines := pWE^.hLines^;
  630.         nLines := pWE^.nLines;
  631.         startLine := _WEPinInRange(startLine, 0, nLines);
  632.         endLine := _WEPinInRange(endLine, 0, nLines);
  633.         _WEReorder(startLine, endLine);
  634.         WEGetHeight := pLines^[endLine].lineOrigin - pLines^[startLine].lineOrigin;
  635.     end;  { WEGetHeight }
  636.  
  637.     function WEGetText (hWE: WEHandle): Handle;
  638.     begin
  639.         WEGetText := hWE^^.hText;
  640.     end;  { WEGetText }
  641.  
  642.     function WEGetChar (offset: LongInt;
  643.                                     hWE: WEHandle): Char;
  644.         var
  645.             pWE: WEPtr;
  646.     begin
  647.         WEGetChar := Char(0);
  648.         pWE := hWE^;
  649.  
  650. { sanity check: make sure offset is withing allowed bounds }
  651.         if ((offset < 0) or (offset >= pWE^.textLength)) then
  652.             Exit(WEGetChar);
  653.  
  654. { get the specified character (actually, byte) }
  655.         WEGetChar := WECharsHandle(pWE^.hText)^^[offset];
  656.  
  657.     end;  { WEGetChar }
  658.  
  659. end.